/***************************************************************************
 *  Copyright (C) 2009, 2010 by Axel Jaeger <axeljaeger@googlemail.com>    *
 *                                                                         *
 *  This file is part of Glowworm.                                         *
 *                                                                         *
 *  Glowworm is free software: you can redistribute it and/or modify       *
 *  it under the terms of the GNU General Public License as published by   *
 *  the Free Software Foundation, version 3 of the License.                *
 *                                                                         *
 *  Glowworm is distributed in the hope that it will be useful,            *
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of         *
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          *
 *  GNU General Public License for more details.                           *
 *                                                                         *
 *  You should have received a copy of the GNU General Public License      *
 *  along with Glowworm.  If not, see <http://www.gnu.org/licenses/>.      *
 ***************************************************************************/

#include "HttpServer.h"

#include "HttpRequest.h"
#include "HttpResponse.h"
#include "FileServlet.h"
#include "LogModel.h"

#include <QCoreApplication>
#include <QFile>
#include <QStringList>
#include <QTextCodec>
#include <QTcpServer>
#include <QTcpSocket>
#include <QUrl>
#include <QHostInfo>

HttpServer::HttpServer(QObject* parent)
    : QObject(parent)
    , m_tcpServer(new QTcpServer(this))
    , m_port(8080)
    , m_fileServlet(new FileServlet(this))
    , m_logModel(new LogModel(this))

{
    Q_CHECK_PTR(m_tcpServer);
    connect(m_tcpServer, SIGNAL(newConnection()), this, SLOT(handleIncomingConnection()));
}

HttpServer::~HttpServer()
{
    delete m_tcpServer;
}

void HttpServer::handleIncomingConnection()
{
    while (m_tcpServer->hasPendingConnections()) {
        QTcpSocket* tcpSocket = m_tcpServer->nextPendingConnection();
        Q_CHECK_PTR(tcpSocket);

        if (tcpSocket->isValid())
        {
            connect(tcpSocket, SIGNAL(readyRead()), this, SLOT(handleRead()));
            connect(tcpSocket, SIGNAL(disconnected()), this, SLOT(handleDisconnect()));
       } else {
            delete(tcpSocket);
       }
    }
}

void HttpServer::handleRead()
{
    QTcpSocket* tcpSocket = (QTcpSocket*) QObject::sender();
    Q_CHECK_PTR(tcpSocket);

    if (tcpSocket->canReadLine())
    {
        QStringList tokens = QString(tcpSocket->readLine()).split(QRegExp("[ \r\n][ \r\n]*"));

        QString localhost(QHostInfo::localHostName());
        QUrl localUrl(QString("http://%1/").arg(localhost));
        QUrl queryUrl(localUrl.resolved(QUrl(tokens.at(1))));

        HttpRequest request;
        request.setMethod(tokens.at(0));

        QMap<QString, QString> map;
        QList<QPair<QString, QString> > qitms(queryUrl.queryItems());
        for (int i = 0; i < qitms.count(); ++i) {
            QPair<QString,QString> pair(qitms.at(i));
            map.insert(pair.first, pair.second);
        }

        request.setParameters(map);
        request.setUrl(queryUrl);

        HttpResponse response;
        response.setOutputDevice(tcpSocket);

        AbstractServlet* handler(m_fileServlet);

        logModel()->log(request.url().path());

        QString path(request.url().path());

        foreach (const QString & key, m_servlets.keys()) {
            if (path.startsWith(key)) {
                handler = m_servlets.value(key);
                break;
            }
        }

        handler->request(&request, &response);
        if (!response.headerSent()) {
            response.sendHeader();
        }
        tcpSocket->close();
    }
}

QString HttpServer::statusMessage(int statusCode, const QString& contentType)
{
    QString description;
    switch(statusCode) {
        case 200:
            return QString("HTTP/1.0 200 OK\r\nContent-Type: %1\r\n\r\n").arg(contentType);
        case 403:
            description = QString("403 Forbidden");
            break;
        case 404:
            description = QString("404 File Not Found");
            break;
        case 500:
            description = QString("500 Internal Server Error");
            break;
    }

    return QString("HTTP/1.0 %1\r\nContent-Type: text/html\r\n\r\n<html><body><h1>%1</h1></body></html>\r\n").arg(description);
}

void HttpServer::handleDisconnect()
{
    QTcpSocket* tcpSocket(qobject_cast<QTcpSocket*>(QObject::sender()));
    Q_ASSERT(tcpSocket != NULL);

    if (! tcpSocket->isValid())
        tcpSocket->deleteLater();
}

bool HttpServer::enabled() const
{
    return m_tcpServer->isListening();
}

void HttpServer::setPort(int port)
{
    m_port = port;
}

int HttpServer::port() const
{
    return m_port;
}

LogModel* HttpServer::logModel() const
{
    return m_logModel;
}

void HttpServer::setEnabled(bool enable)
{
    if (enable == enabled()) {
        return;
    }

    if (enable) {
        m_tcpServer->listen(QHostAddress::Any, port());

    } else {
        m_tcpServer->close();
    }
}

QString HttpServer::wwwRoot() const
{
    return m_wwwRoot;
}

void HttpServer::setWwwRoot(const QString & dir)
{
    m_wwwRoot = dir;
    m_fileServlet->setWwwRoot(dir);
}

void HttpServer::addServlet(const QString & key, AbstractServlet* servlet)
{
    m_servlets.insert(key, servlet);
}
